package
{	
	import flash.display.*;
	import flash.events.*;
	import flash.geom.Point;
	import flash.ui.Keyboard;
	import flash.ui.Mouse;
	import flash.xml.*;
	
	/** Object allows user to draw on a canvas in either of two major modes: Free and Connected.
	Free mode: pen draws as user pressed and holds, fills up pixels underneath pen, eraser does same
	Connected mode: pen connects from a highlighted point, click off points to draw new point (if allowed)
	*/
	public class DrawingPanel extends MovieClip
	{
		//public static const BACK_COLOR:uint = 0xF6F6FF;
		//public static const OUTLINE_COLOR:uint = 0x8888FF;
		public static const BACK_COLOR:uint = 0xE0FFD0;
		public static const OUTLINE_COLOR:uint = 0x88FF88;
		public static const PAINT_COLOR:uint = 0x000000;
		public static var MINIMUM_SEGMENT_LENGTH:int;
		public var WIDTH:Number, HEIGHT:Number;
		public var parallel_threshold_degrees:Number, length_threshold_pixels:Number, angle_threshold_degrees:Number, right_threshold_degrees:Number;
		public var pointsize:Number, enhancedsize:Number, segmentsize:Number, erasersize:Number;
		public var tool_threshold_multiplier:Number;
		// private objects
		private var xml:XML;
		private var oobshapes:Array, drawPoints:Array, drawSegments:Array, drawAngles:Array, closedPolygons:Array;
		//private var cursor:Sprite;
		// private vars
		private var disconnectedpoints:Boolean, dragpoints:Boolean, outOfBounds:Boolean = false, outOfPanel:Boolean = false;
		private var pointCount:uint = 0, segmentCount:uint = 0, angleCount:uint = 0; //a monotonically increasing count of points used to create a unique id
		private var currentDrawState:int, totalDrawStates:int;
		// Segment (pen-mode) 
		private var overPoint:DrawPoint = null;
		private var selectedDrawPoint:DrawPoint = null;
		private var selectedDrawSegments:Array;
		private var defaultCursorType:String, previousCursorType:String, currentCursorType:String="pen";
		private var dragging_point:Boolean = false;
		private var pen_mode:Boolean = false, drag_mode:Boolean = false, delete_mode:Boolean = false, pin_mode:Boolean = false, outline_mode:Boolean = false;
		// Property Tools
		private var overSegment:DrawSegment = null;
		private var selectedSegment:DrawSegment = null;
		private var overAngle:DrawAngle = null;
		private var selectedAngle:DrawAngle = null;
		//private var cursor_parallel:ParallelTool;
		private var cursor_tool:CursorTool;
		private var length_mode:Boolean=false,angle_mode:Boolean=false,right_mode:Boolean=false, parallel_mode:Boolean = false;
		// relating to the creation of child shapes;
		//private var currentOutlineDrawPoints:Array = null;
		private var currentClosedPolygon:ClosedPolygon;
		private var currentChildPolygon:ChildPolygon = null;
		private var currentChildPolygonType:String = "";
		
		/** WIDTH and HEIGHT obvious, xml provides information about existing points, connectedMode connects points, 
		allowDisconnectedPoints allows users to click a new point of main structure to create a second structure */
		public function DrawingPanel (WIDTH:Number, HEIGHT:Number, xml:XML, connectedMode:Boolean=true, allowDisconnectedPoints:Boolean=false)
		{
			this.WIDTH = WIDTH;
			this.HEIGHT = HEIGHT;
			this.xml = xml;
			this.disconnectedpoints = true;
			this.dragpoints = true;
			
			// get thresholds, and multiplier for threshold to use tools
			if (xml.attribute("parallel_threshold_degrees").length()>0){parallel_threshold_degrees = Number(xml.attribute("parallel_threshold_degrees")[0]);}
			else {parallel_threshold_degrees = 1;}
			
			if (xml.attribute("length_threshold_pixels").length()>0){length_threshold_pixels = Number(xml.attribute("length_threshold_pixels")[0]);}
			else {length_threshold_pixels = 1;}
			
			if (xml.attribute("angle_threshold_degrees").length()>0){angle_threshold_degrees = Number(xml.attribute("angle_threshold_degrees")[0]);}
			else {angle_threshold_degrees = 1;}
			
			if (xml.attribute("right_threshold_degrees").length()>0){right_threshold_degrees = Number(xml.attribute("right_threshold_degrees")[0]);}
			else {right_threshold_degrees = 1;}
			
			if (xml.attribute("tool_threshold_multiplier").length()>0){tool_threshold_multiplier = Number(xml.attribute("tool_threshold_multiplier")[0]);}
			else {tool_threshold_multiplier = 20;}
			
			
			if (xml.attribute("pointsize").length()>0){pointsize = Number(xml.attribute("pointsize")[0]);} 
			else {pointsize = 10;}
			
			if (xml.attribute("segmentsize").length()>0){segmentsize = Number(xml.attribute("segmentsize")[0]);} 
			else{ segmentsize = 4; }
			
			if (xml.attribute("erasersize").length()>0){erasersize = Number(xml.attribute("erasersize")[0]);}
			else {erasersize = pointsize*2;}
			this.enhancedsize = pointsize * 3/2;
			
			
			
			MINIMUM_SEGMENT_LENGTH = pointsize;
			this.addEventListener(Event.ADDED, handleAdded);
		}
		private function handleAdded(event:Event):void
		{
			this.removeEventListener(Event.ADDED, handleAdded);
			// Give this a light blue hue 
			this.graphics.lineStyle(1, OUTLINE_COLOR);
			this.graphics.beginFill(BACK_COLOR);
			this.graphics.drawRect(0,0,WIDTH,HEIGHT);
			this.graphics.endFill();
			oobshapes = new Array();
			drawPoints = new Array();
			drawSegments = new Array();
			drawAngles = new Array();
			selectedDrawSegments = new Array();
			closedPolygons = new Array();
			// add after the rest of the stuff so that cursor is on top
			// what's the last draw state?
			totalDrawStates = xml..drawstate.length();
			currentDrawState = totalDrawStates-1;
			
			drawOobShapesFromXML();
			drawPointsFromXML(currentDrawState);
			drawSegmentsFromXML(currentDrawState);
			selectPointFromXML(); // need to do this after so that points are attached to segments
			findAllClosedPolygons ();
			shiftModes("pen_mode", this);
			
			this.stage.addEventListener(KeyboardEvent.KEY_DOWN, handleKeyboard);
			this.addEventListener(Event.ENTER_FRAME, handleFrame); // does a lot of stuff
			
		}
		/** Iterate through list of out of bound shapes, and their points to create shapes on which a point may not be placed */
		public function drawOobShapesFromXML():void
		{
			//trace(xml..oob.length());
			for each (var node:XML in xml..oob[0]..oobshape)
			{
				// only bother if we have more than two points in this shape
				if (node..oobpoint.length() >= 3)
				{
					var _points:Array = new Array();
					for each (var pxml:XML in node..oobpoint)
					{
						if (pxml.attribute("x").length() > 0){var px:Number = pxml.attribute("x")[0];}
						else { break; }
						if (pxml.attribute("y").length() > 0){var py:Number = pxml.attribute("y")[0];}
						else { break; }
						var p:Point = new Point(px, py);
						_points.push(p);
					}
					var oobshape:OobShape = new OobShape (_points);
					oobshapes.push(oobshape);
					this.addChild(oobshape);
				}
			}
		}
		/** Iterate through list of initial points in xml doc */
		public function drawPointsFromXML(drawStateIndex:int):void
		{			
			for each (var node:XML in xml..drawstate[drawStateIndex]..point)
			{
				var pid:String="";
				if ( node.attribute("id").length() > 0 ) pid = node.attribute("id")[0];
				var pinned:Boolean=false, fixed:Boolean=false;
				// can this point be deleted?
				if ( node.attribute("pinned").length() > 0 && String(node.attribute("pinned")[0])=="true") pinned = true;
				if ( node.attribute("fixed").length() > 0 && String(node.attribute("fixed")[0])=="true") fixed = true;
				var px:Number = Number(node.attribute("x")[0]);
				var py:Number = Number(node.attribute("y")[0]);
				
				if (true)
				{ // draw a point place on panel directly
					var p:DrawPoint = newPoint(pid, pinned, fixed);
					if (px >= 1 || py >= 1){ p.x = px; } else {p.x = this.WIDTH * px}
					if (py >= 1 || py >= 1){ p.y = py; } else {p.y = this.HEIGHT * py}
				} 
			}			
		}
		/** Iterate through list of initial segments in xml doc */
		public function drawSegmentsFromXML(drawStateIndex:int):void
		{
			for each (var node:XML in xml..drawstate[drawStateIndex]..segment)
			{
				var sid:String="";
				if ( node.attribute("id").length() > 0 ) sid = node.attribute("id")[0];
				
				var p1:DrawPoint = lookupPointByID(node.attribute("p1")[0]);
				var p2:DrawPoint = lookupPointByID(node.attribute("p2")[0]);
				if (p1 != null && p2 != null)
				{
					newSegment(p1, p2, sid);
				} else
				{
					trace("In constructing segment, point was not found, check xml");
				}
			}			
		}
		/** Go through and select the point to initially begin drawing */
		private function selectPointFromXML():void
		{
			var _selectPointID:String = "";
			if ( xml.attribute("selectedDrawPoint").length() > 0) _selectPointID = String(xml.attribute("selectedDrawPoint")[0]);
			if (selectPoint.length > 0)
			{
				for each (var p:DrawPoint in drawPoints)
				{
					if (p.id == _selectPointID)
					{
						selectPoint(p);
						break;
					}
				}
			}
			// in the case of undisconnected points we want to just select the last point
			if (selectedDrawPoint == null && !disconnectedpoints)
			{
				selectPoint(p);
			}
		}
		
		/////////////////////////////////////////////////////////// CONSTRUCTION FUNCTIONS ///////////////////////////////////////////////////////////////
		//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		
		
		/** Create a new point with a unique id or given id
		 */ 
		public function newPoint(id:String="no", pinned:Boolean = false, fixed:Boolean = false):DrawPoint
		{
			// update point count
			pointCount++;
			
			var pid:String;
			if (id != "no")
			{ // use given pid
				pid = id;
			} else
			{ // search for a unique name
				pid = String("p"+ pointCount)
			}
			var p:DrawPoint = new DrawPoint(pointsize, pid, pinned, fixed);
			drawPoints.push(p);
			if (cursor_tool == null)
			{
				this.addChild(p);	
			} else
			{
				this.addChildAt(p,this.numChildren-1);	
			}
			findAllClosedPolygons();		
			return p;
		}
		/** This function selects a current DrawPoint (which should be in the array)
		* Likewise selects all of the segments attached to the point (in arbitrary order)
		*/
		public function selectPoint(p:DrawPoint):void
		{
			if (true)
			{
				deselectCurrentPoint();
				p.highlight = true;
				selectedDrawPoint = p;
				// go through segment array and check whether any of the segments contain this point
				for each (var s:DrawSegment in drawSegments)
				{
					if (s.containsDrawPoint(p))
					{
						selectedDrawSegments.push(s);
					}
				}
			}
		}
		/** Deselects the currently selected point, does not replace with another point */
		public function deselectCurrentPoint():void
		{
			if (true)
			{
				if (selectedDrawPoint != null)
				{
					selectedDrawPoint.highlight = false;
				}
				selectedDrawPoint = null;
				selectedDrawSegments = new Array();
			}
		}
		
		/** Delete point deletes points and all associated segments and angles */
		public function deletePoint(p:DrawPoint):void
		{
			if (p != null && p.parent == this)
			{
				var s:DrawSegment;
				// first get rid of any segments that are attached to this point
				for (var i:int=drawSegments.length-1; i >= 0; i--)
				{
					s = drawSegments[i];
					//trace("segment", i,":", s.containsDrawPoint(p));
					if (s.containsDrawPoint(p))
					{
						// also get rid of segment reference in other points
						s.point2.removeSegment(s);
						s.point1.removeSegment(s);
						drawSegments.splice(i,1);
						this.removeChild(s);
					}
				}
				selectedDrawSegments = new Array();
				
				var a:DrawAngle;
				// then angles
				for (i=drawAngles.length-1; i >= 0; i--)
				{
					a = drawAngles[i]
					if (a.containsDrawPoint(p))
					{
						// also get rid of segment reference in other points
						a.point2.removeAngle(a);
						a.point1.removeAngle(a);
						a.pointC.removeAngle(a);
						drawAngles.splice(i,1);
						this.removeChild(a);
					}
				}
				
				if (p==selectedDrawPoint) selectedDrawPoint = null;
				if (p==overPoint) overPoint = null;
				//if (p.hasEventListener(MouseEvent.MOUSE_OVER)) p.removeEventListener(MouseEvent.MOUSE_OVER, handleMouseOverPoint);
				//if (p.hasEventListener(MouseEvent.MOUSE_OUT)) p.removeEventListener(MouseEvent.MOUSE_OUT, handleMouseOutPoint);
				//if (p.hasEventListener(MouseEvent.CLICK)) p.removeEventListener(MouseEvent.CLICK, handleClickPoint);
				//if (p.hasEventListener(MouseEvent.MOUSE_DOWN)) p.removeEventListener(MouseEvent.MOUSE_DOWN, handleMouseDownPoint);
				//if (p.hasEventListener(MouseEvent.MOUSE_UP)) p.removeEventListener(MouseEvent.MOUSE_UP, handleMouseUpPoint);
				
				drawPoints.splice(drawPoints.indexOf(p),1);
				this.removeChild(p);
				findAllClosedPolygons();
			}
		}
		/** Will create a new segment between two points, but also ensures that the segment is unique */
		public function newSegment(p1:DrawPoint, p2:DrawPoint, id:String = "no", overrideUniquenessCheck:Boolean = false):DrawSegment
		{
			var s:DrawSegment;
			// Make sure this segment is not already created (i.e., has both points)
			var isNewSegment:Boolean = true;
			if (!overrideUniquenessCheck)
			{
				for each (s in drawSegments)
				{
					if (s.containsDrawPoint(p1) && s.containsDrawPoint(p2))
					{
						isNewSegment = false; break;
					}
				}
			}
			if (isNewSegment)
			{
				segmentCount++;
				var sid:String;
				if (id != "no")
				{ // use given pid
					sid = id;
				} else
				{ // search for a unique name
					sid = String("s"+ segmentCount)
				}
				s = new DrawSegment(segmentsize, p1, p2, sid);				
				drawSegments.push(s);
				this.addChildAt(s, oobshapes.length);
				// if this segment has any adjacent segments, i.e., segments attached to this segments point1 or point2, create drawAngles
				var _s:DrawSegment;
				var a:DrawAngle;
				if (p1.segmentcount > 1)
				{
					for each (_s in p1.drawSegments)
					{
						if (s != _s)
						{
							a = new DrawAngle(s, _s);
							//a.drawArc(20);
							this.addChildAt(a, drawSegments.length);
							drawAngles.push(a);
						}
					}
				}
				if (p2.segmentcount > 1)
				{
					for each (_s in p2.drawSegments)
					{
						if (s != _s)
						{
							a = new DrawAngle(s, _s);
							//a.drawArc(20);
							this.addChildAt(a, drawSegments.length);
							drawAngles.push(a);
						}
					}
				}
			}
			
			findAllClosedPolygons();
			return(s);
		}
		
		/** Function finds all closed polygons on the panel by iterating through all points and segements. Adds to array. Returns number of closed polygons. */
		public function findAllClosedPolygons():void
		{
			clearAllClosedPolygons ();
			// iterate through all points
			for (var i:int=0; i < drawPoints.length; i++)
			{
				var dp:DrawPoint = drawPoints[i];
				// use recursive function to follow tree of all segments
				followSegmentTree(new Array(dp));
			}
		}
			private function followSegmentTree(prevDrawPoints:Array):Boolean
			{
				var newDrawPoints:Array = null
				var rootdp:DrawPoint = prevDrawPoints[0];
				var dp:DrawPoint = prevDrawPoints[prevDrawPoints.length-1];
				for (var i:int=0; i < dp.drawSegments.length; i++)
				{
					var newpoint:DrawPoint = null;
					var ds:DrawSegment = dp.drawSegments[i];
					// two cases:
					// 1) This is the first call, we cannot close based on one segment
					if (prevDrawPoints.length == 1)
					{
						// which of the two points is the newpoint?
						if (ds.point1 == dp && ds.point2 != dp){ newpoint = ds.point2;}
						else if (ds.point1 != dp && ds.point2 == dp){ newpoint = ds.point1;}
						if (newpoint != null)
						{
							newDrawPoints = prevDrawPoints.slice();
							newDrawPoints.push(newpoint);
							//trace("1", newDrawPoints);
							followSegmentTree(newDrawPoints);
						} else
						{
							return false;
						}
					} // 2) We are one segment removed from the root, we cannot link back to it yet, make sure new point is not root point
					else if (prevDrawPoints.length == 2)
					{
						// which of the two points is the newpoint?
						if (ds.point1 == dp && ds.point2 != dp && ds.point2 != rootdp){ newpoint = ds.point2;}
						else if (ds.point1 != dp && ds.point2 == dp && ds.point1 != rootdp){ newpoint = ds.point1;}
						if (newpoint != null)
						{
							newDrawPoints = prevDrawPoints.slice();
							newDrawPoints.push(newpoint);
							//trace("2", newDrawPoints);
							followSegmentTree(newDrawPoints);
						} else
						{
							return false;
						}
					}
					// 3) We have at least two removed, we can connect back to the root point and construct a new closed polygon
					else if (prevDrawPoints.length >= 3)
					{
						var j:uint;
						// which of the two points is the newpoint?
						if (ds.point1 == dp && ds.point2 != dp){ newpoint = ds.point2;}
						else if (ds.point1 != dp && ds.point2 == dp){ newpoint = ds.point1;}
						
						//trace("newpoint vs. root", newpoint.x, newpoint.y, rootdp.x, rootdp.y, newpoint==rootdp);
						//for (j=prevDrawPoints.length-1; j > 0; j--)	
						//{
						//	trace("oldpoint vs. root", prevDrawPoints[j].x, prevDrawPoints[j].y, rootdp.x, rootdp.y, prevDrawPoints[j]==rootdp); 
						//}
						
						if (newpoint == rootdp) 
						{ 
							newDrawPoints = prevDrawPoints.slice();
							return(newClosedPolygon(newDrawPoints));
						}						
						else // new point is not a return to root point, but have we seen it before, if not continue the traverse
						{	// skip root and current point
							var found:Boolean = false;
							for (j=1; j < prevDrawPoints.length-1; j++)	
							{
								var prevdp:DrawPoint = prevDrawPoints[j];
								if (prevdp == newpoint)
								{
									found = true;
									break;
								}
							}
							if (!found)
							{
								newDrawPoints = prevDrawPoints.slice();
								newDrawPoints.push(newpoint);
								//trace("3+", newDrawPoints);
								followSegmentTree(newDrawPoints);
							} else
							{
								return false;
							}
						}
					}
				}
				return false;
			}
		
		/** Constructs a new closed polygon with a set of drawpoints if those points do not already make one, returns true if new closed polygon is made and added to array */
		public function newClosedPolygon (_drawPoints:Array):Boolean
		{
			var _points:Array = DrawPoint.toGeomPoints(_drawPoints);
			//trace(_points);
			// go through all closed polygons and make sure this set of points is new
			var found:Boolean = false;
			//trace("new polygon? yes or no?");
			for each (var cpoly:ClosedPolygon in closedPolygons)
			{
				if (cpoly.containsEquivalentPoints(_points))
				{
					found = true;
					break;
				}
			}
			if (!found)
			{
				//trace("yes");
				var closedPolygon:ClosedPolygon = new ClosedPolygon(_drawPoints);
				closedPolygons.push(closedPolygon);
				this.addChild(closedPolygon);
				return true;
			} else
			{
				//trace("no");
				return false;
			}
		}
		
		public function clearAllClosedPolygons ():void
		{
			for each (var p:ClosedPolygon in closedPolygons)
			{
				if (p.parent == this) this.removeChild(p);
			}
			closedPolygons = new Array();
		}
		
			/** Removes all points drawAngles and segments from the field */
		public function clear():void
		{
			for (var i:int=drawPoints.length-1; i >= 0; i--)
			{
				var p:DrawPoint = drawPoints[i];
				deletePoint(p);
			}
			closedPolygons = new Array();
		}
		
		
		
		///////////////////////////////////////////////////// OTHER FUNCTIONS FOR POLYGON COMPONENTS     //////////////////////////////////////////
		
		/** Go through array of points to find one that matches the given id */
		private function lookupPointByID(pid:String):DrawPoint
		{
			for each (var p:DrawPoint in drawPoints)
			{
				if (p.id == pid) return p;
			}
			return null;
		}
		
		/** This function is used to determine if a point is a valid stopping point for a DrawPoint */
		public function isValidCursorPoint (_x:Number, _y:Number):Boolean
		{
			if (_x > 0 && _x < this.WIDTH && _y > 0 && _y < this.HEIGHT)
			{
				if (cursor_tool != null)
				{
					if (!cursor_tool.outOfBounds)
					{
						return true;
					} else
					{
						return false;
					}
				} else
				{
					return true;
				}
			} else
			{
				return false;				
			}
		}
		/** This function is used to determine if a point is a valid stopping point for a DrawPoint */
		public function isValidPoint (_x:Number, _y:Number):Boolean
		{
			if (_x > 0 && _x < this.WIDTH && _y > 0 && _y < this.HEIGHT)
			{
				var _outOfBounds:Boolean = true;
				// check to see if this point is in a shape
				var gpoint:Point = new Point(_x, _y);
				gpoint = this.localToGlobal(gpoint);
				for each (var oobshape:OobShape in oobshapes)
				{
					if (oobshape.hitTestPoint(gpoint.x, gpoint.y, true)) return false;
				}
				return true;
			} else
			{
				return false;				
			}
		}
		/*
		// out of bounds objects
		private function handleMouseOverOobShape(event:MouseEvent):void
		{
			event.currentTarget.removeEventListener(MouseEvent.MOUSE_OVER, handleMouseOverOobShape);
			outOfBounds = true;
			if (pen_mode)
			{
				updateCursorLook("pen-no");
			} else if (drag_mode)
			{
				updateCursorLook("drag-no");
			}
			event.currentTarget.addEventListener(MouseEvent.MOUSE_OUT, handleMouseOutOobShape);
		}
		private function handleMouseOutOobShape(event:MouseEvent):void
		{
			event.currentTarget.removeEventListener(MouseEvent.MOUSE_OUT, handleMouseOutOobShape);
			outOfBounds = false;
			if (pen_mode) 
			{
				updateCursorLook(defaultCursorType);
			} else if (drag_mode)
			{
				updateCursorLook("drag-yes");
			}
			event.currentTarget.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverOobShape);
		}
		*/
		///////////////////////////////////////// INTERACTIVITY //////////////////////////////////////////////////////
		
		//////////////////////  SEGMENT MODE - EG PEN MODE //////////////////////////////
		
		/** Mouse over should work for all points to highlight except currently selected, unless allow disconnected */
		/*
		private function handleMouseOverPoint(event:MouseEvent):void
		{
			trace("calling parent");
			overPoint = DrawPoint(event.currentTarget);
			overPoint.removeEventListener(MouseEvent.MOUSE_OVER, handleMouseOverPoint);
			// If this point is not selected, wait for press in drag_mode wait, for click in non_drag mode
			if (drag_mode)
			{
				if (!overPoint.pinned)
				{
					updateCursorLook("drag-yes");
					overPoint.enhance = true;
					overPoint.addEventListener(MouseEvent.MOUSE_DOWN, handleMouseDownPoint);
				} else
				{
					updateCursorLook("drag-no");
				}
			} else if (pen_mode)
			{ // not dragging
				if (overPoint != selectedDrawPoint || disconnectedpoints)
				{
					updateCursorLook("pen-yes");
					overPoint.enhance = true;
					overPoint.addEventListener(MouseEvent.CLICK, handleClickPoint);
					//trace("mouse click point", event.currentTarget, event.target);
				} 			
			} else if (delete_mode)
			{
				if (!overPoint.pinned)
				{
					updateCursorLook("delete-yes");
					overPoint.addEventListener(MouseEvent.CLICK, handleClickPoint);
				} else 
				{
					updateCursorLook("delete-no");
				}
			} else if (pin_mode)
			{
				if (!overPoint.fixed)
				{
					updateCursorLook("pin-yes");
					overPoint.addEventListener(MouseEvent.CLICK, handleClickPoint);
				} else 
				{
					updateCursorLook("pin-no");
				}
			} 
			//else if (outline_mode)
			//{
			//	overPoint.enhance = true;
			//	overPoint.addEventListener(MouseEvent.CLICK, handleClickPoint);
			//}
			overPoint.addEventListener(MouseEvent.MOUSE_OUT, handleMouseOutPoint);
				
			// Even If this point is selected still need an out handler
			
		}
		private function handleMouseOutPoint(event:MouseEvent):void
		{
			overPoint.removeEventListener(MouseEvent.MOUSE_OUT, handleMouseOutPoint);
			if (drag_mode)
			{
				// in the case where the point we are over is highlighted remove the clicking potential and change back highlighting				
				overPoint.removeEventListener(MouseEvent.MOUSE_DOWN, handleMouseDownPoint);
				overPoint.enhance = false;
				if (!dragging_point)
				{
					updateCursorLook(defaultCursorType);
					
				}
			} else if (pen_mode)
			{
				if (overPoint != selectedDrawPoint || disconnectedpoints)
				{
					updateCursorLook(defaultCursorType);
					overPoint.removeEventListener(MouseEvent.CLICK, handleClickPoint);
					overPoint.enhance = false;
				}				
			} else if (delete_mode || pin_mode)
			{
				updateCursorLook(defaultCursorType);
				overPoint.removeEventListener(MouseEvent.CLICK, handleClickPoint);
				overPoint.enhance = false;
			}
			//else if (outline_mode)
			//{
			//	overPoint.removeEventListener(MouseEvent.CLICK, handleClickPoint);
			//	overPoint.enhance = false;
			//}
			overPoint.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverPoint);
			overPoint = null;
		}
		*/
		/* The click handler either adds a new segment if another point is selected or selects this point if no point is selected */
		/*
		private function handleClickPoint(event:MouseEvent):void
		{
			//overPoint.removeEventListener(MouseEvent.MOUSE_OUT, handleMouseOutPoint);
			overPoint.removeEventListener(MouseEvent.CLICK, handleClickPoint);
			
			if (pen_mode)
			{
				// There is a point selected, draw a new segment to the overpoint, or if overpoint is the same selected, (if allowed) deselect it
				if (selectedDrawPoint != null)
				{ // If the point we are over is not selected, draw segment to it
					if (overPoint != selectedDrawPoint)
					{
						newSegment(selectedDrawPoint, overPoint);
						this.saveNewDrawState("pen");
						selectPoint(overPoint);
					} else if (disconnectedpoints)
					{  // If we are over the selected points, and we allow disconnected points, deselect it
						deselectCurrentPoint();
					}
				} else
				{ // no point is selected, so select this one (which should only be the case in disconnected points mode (by default, but I won't enforce)
					selectPoint(overPoint);			
				}
				//overPoint = null;
				overPoint.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverPoint);
			} 
			else if (delete_mode)
			{
				if (!overPoint.pinned)
				{
					if(overPoint.hasEventListener(MouseEvent.MOUSE_OUT)) overPoint.removeEventListener(MouseEvent.MOUSE_OUT, handleMouseOutPoint);
					this.deletePoint(overPoint);
					this.saveNewDrawState("delete");
					updateCursorLook(defaultCursorType);					
				}
			}	
			else if (pin_mode)
			{
				if (!overPoint.fixed)
				{
					if(overPoint.hasEventListener(MouseEvent.MOUSE_OUT)) overPoint.removeEventListener(MouseEvent.MOUSE_OUT, handleMouseOutPoint);
					if (overPoint.pinned) { overPoint.pin = false; }
					else { overPoint.pin = true;  }		
					this.saveNewDrawState("pin");
					updateCursorLook(defaultCursorType);					
				}
				overPoint.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverPoint);
			}	
			//else if (outline_mode)
			//{
			//	if(overPoint.hasEventListener(MouseEvent.MOUSE_OUT)) overPoint.removeEventListener(MouseEvent.MOUSE_OUT, handleMouseOutPoint);
			//	overPoint.enhance = false; // do this before processing the cursor_tool so that it is back to its normal color
			//	cursor_tool.selectComponent(overPoint);
			//	overPoint.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverPoint);
			//}
		}
		*/
		/** For dragging of point in drag_mode */
		/*
		private function handleMouseDownPoint(event:MouseEvent):void
		{
			overPoint.removeEventListener(MouseEvent.MOUSE_DOWN, handleMouseDownPoint);
			// There is a point selected, draw a new segment to the overpoint, or if overpoint is the same selected, (if allowed) deselect it
			if (overPoint != selectedDrawPoint)
			{
				selectPoint(overPoint);
			}		
			if (!overPoint.pinned)
			{
				dragging_point = true;
				// we need to make this unenabled so that we can tell if we go over an oobshape
				selectedDrawPoint.mouseEnabled = false;
			}
			parent.addEventListener(MouseEvent.MOUSE_UP, handleMouseUpPoint);			
		}
		*/
		/** For dragging of point in drag_mode */
		/*
		private function handleMouseUpPoint(event:MouseEvent):void
		{
			parent.removeEventListener(MouseEvent.MOUSE_UP, handleMouseUpPoint);
			dragging_point = false;
			selectedDrawPoint.mouseEnabled = true;
			if (overPoint != null)
			{
				overPoint.addEventListener(MouseEvent.MOUSE_DOWN, handleMouseDownPoint);				
			}
			saveNewDrawState("drag");
		}
		*/		
		/********* FOR SEGMENT DRAWING (PEN MODE) ***********/
		/** If a point is selected draw a new point to this location, if a point is not selected just draw a new point */
		/*
		private function handleClickEmptyPanel(event:MouseEvent):void
		{
			var p:DrawPoint;
			// overPoint should be null be default here, but just checking
			// DRAW NEW POINT AND SEGMENT
			if (pen_mode)
			{
				if (!outOfBounds)
				{
					if (selectedDrawPoint != null && overPoint == null)
					{
						p = newPoint();
						// we only want to add a listener if we are in the segment mode, will erase in the point mode using a different means
						if (true) p.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverPoint);
						p.moveTo(this.mouseX, this.mouseY);
						newSegment(p, selectedDrawPoint);
						selectPoint(p);
					} else if (selectedDrawPoint == null && overPoint == null)
					{ // overPoint should still be null DRAW NEW POINT ONLY
						p = newPoint();
						// we only want to add a listener if we are in the segment mode, will erase in the point mode using a different means
						if (true) p.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverPoint);
						p.moveTo(this.mouseX, this.mouseY);
						selectPoint(p);
					}
					saveNewDrawState("pen");
				}
			}
		}
		*/
	///////////////////////////////////////// PROPERTY TOOL FUNCTIONS //////////////////////////////////////
	/////////////////////////////////////////////SEGMENT TOOLS///////////////////////////////////////////////////
		
		
		/** Mouse over should work for all segments to highlight except currently selected, unless allow disconnected */
		/*
		private function handleMouseOverSegment(event:MouseEvent):void
		{
			overSegment = DrawSegment(event.currentTarget);
			overSegment.removeEventListener(MouseEvent.MOUSE_OVER, handleMouseOverSegment);
			overSegment.addEventListener(MouseEvent.MOUSE_OUT, handleMouseOutSegment);
			overSegment.addEventListener(MouseEvent.CLICK, handleClickSegment);
		}
		private function handleMouseOutSegment(event:MouseEvent):void
		{
			overSegment.removeEventListener(MouseEvent.MOUSE_OUT, handleMouseOutSegment);
			overSegment.removeEventListener(MouseEvent.CLICK, handleClickSegment);
			overSegment.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverSegment);
			overSegment = null;
		}
		private function handleClickSegment (event:MouseEvent):void
		{
			if (selectedSegment != overSegment)
			{
				selectedSegment = overSegment;
				if (parallel_mode || length_mode)
				{
					cursor_tool.selectComponent(selectedSegment);
				}  
			} else
			{
				selectedSegment = null;
				if (parallel_mode || length_mode)
				{
					cursor_tool.clearComponent(selectedSegment);
				}  
			}			
		}
		*/
		///////////////////////////////////// ANGLE TOOLS ///////////////////////////////////////////////
		/** Mouse over should work for all segments to highlight except currently selected, unless allow disconnected */
		/*
		private function handleMouseOverAngle(event:MouseEvent):void
		{
			overAngle = DrawAngle(event.currentTarget);
			overAngle.removeEventListener(MouseEvent.MOUSE_OVER, handleMouseOverAngle);
			overAngle.addEventListener(MouseEvent.MOUSE_OUT, handleMouseOutAngle);
			overAngle.addEventListener(MouseEvent.CLICK, handleClickAngle);
		}
		private function handleMouseOutAngle(event:MouseEvent):void
		{
			overAngle.removeEventListener(MouseEvent.MOUSE_OUT, handleMouseOutAngle);
			overAngle.removeEventListener(MouseEvent.CLICK, handleClickAngle);
			overAngle.addEventListener(MouseEvent.MOUSE_OVER, handleMouseOverAngle);
			overAngle = null;
		}
		private function handleClickAngle (event:MouseEvent):void
		{
			if (selectedAngle != overAngle)
			{
				selectedAngle = overAngle;
				if (angle_mode || right_mode)
				{
					cursor_tool.selectComponent(selectedAngle);
				}  
			} else
			{
				selectedAngle = null;
				if (angle_mode || right_mode)
				{
					cursor_tool.clearComponent(selectedAngle);
				}  
			}			
		}
		*/
	///////////////////////////////////////// COMMON FUNCTIONS /////////////////////////////////////////////	
	/** Function for dragging paint or points */
		private function handleFrame (event:Event):void
		{	
			// Are we in bounds?
			if (this.mouseX >= 0 && this.mouseY >= 0 && this.mouseX <= this.WIDTH && this.mouseY <= this.HEIGHT)
			{
				// just coming back in
				if (outOfPanel) {
					Mouse.hide();
					outOfPanel = false;
					if (cursor_tool != null)
					{
						cursor_tool.alpha = 1;
					}
				}
				if (cursor_tool != null) cursor_tool.process(new Point(this.mouseX, this.mouseY));
			} else
			{
				if (!outOfPanel)
				{
					outOfPanel = true;
					Mouse.show();
					if (cursor_tool != null)
					{
						cursor_tool.alpha = 0;
					}
				}
			}
		}
		
		/** Listening to a CursorTool, handles when complete */
		private function handleToolComplete (event:Event):void
		{
			trace("tool complete");
			cursor_tool.removeEventListener(Event.COMPLETE, handleToolComplete);
			
			var toolName:String;
			if (pen_mode){toolName = "pen";}
			else if	(drag_mode) {toolName = "drag";}
			else if	(pin_mode) {toolName = "pin";}
			else if	(delete_mode) {toolName = "delete";}
			else if	(parallel_mode) {toolName = "parallel";}
			else if (length_mode){toolName = "length";}
			else if (angle_mode){toolName = "angle";}
			else if (right_mode){toolName = "right";}
			else {toolName="none";}
			
			if(toolName !="none")this.saveNewDrawState(toolName);
			
			// in all cases, but outline mode shift into drag mode
			if (pen_mode || drag_mode)
			{
				
			} 
			else if (!outline_mode)
			{
				findAllClosedPolygons();
				shiftModes("drag_mode", this);
			} else
			{
				this.currentClosedPolygon = ClosedPolygon(cursor_tool.components[0]);
				trace(currentClosedPolygon.drawPoints);
				if (currentChildPolygonType != "") newChildPolygon();
			}				
		}
//////////////////////////// FUNCTIONS FOR CHILD SHAPE GROWTH /////////////////////////////////////////////////		
		
		
		
		/** Function called by DrawItem (from gameMenu, originally) to tell panel what shape to grow */
		public function setShapeType(shapeType:String):void 
		{
			currentChildPolygonType = shapeType;
			if (currentClosedPolygon != null)
			{
				newChildPolygon();	
			}			
		}
		/** Sets up the growth of a new shape based on the current shape type selected */
		private function newChildPolygon():void
		{
			if (currentClosedPolygon != null && currentChildPolygonType != "")
			{
				if (cursor_tool != null)
				{
					this.removeChild(cursor_tool);
					cursor_tool = new OutlineTool(closedPolygons);
					cursor_tool.addEventListener(Event.COMPLETE, handleToolComplete);				
					this.addChild(cursor_tool);					
				}
				currentChildPolygon = new ChildPolygon(currentClosedPolygon.drawPoints, currentChildPolygonType);
				currentChildPolygon.addEventListener(Event.COMPLETE, handleChildSuccess);
				currentChildPolygon.addEventListener(Event.CANCEL, handleChildFailure);
				this.addChild(currentChildPolygon);
			}
		}
		/** If the new shape we made meets criteria, this function tells the parent (DrawItem) */
		private function handleChildSuccess(event:Event):void
		{
			currentChildPolygon.removeEventListener(Event.COMPLETE, handleChildSuccess);
			currentChildPolygon.removeEventListener(Event.CANCEL, handleChildFailure);
			GeometryGameItem(parent).handleChildSuccess(currentChildPolygon);
			currentClosedPolygon.outline = false;
			currentClosedPolygon.highlight = false;
			currentClosedPolygon = null;
			this.removeChild(currentChildPolygon);
			currentChildPolygon = null;
			
		}
		/** If the new shape we made does not meet criteria, this function tells the parent (DrawItem) */
		private function handleChildFailure(event:Event):void
		{
			currentChildPolygon.removeEventListener(Event.COMPLETE, handleChildSuccess);
			currentChildPolygon.removeEventListener(Event.CANCEL, handleChildFailure);
			GeometryGameItem(parent).handleChildFailure(currentChildPolygon);
			currentClosedPolygon.outline = false;
			currentClosedPolygon.highlight = false;
			currentClosedPolygon = null;
			this.removeChild(currentChildPolygon);
			currentChildPolygon = null;
			
		}
		
///////////////////////////////// FUNCTIONS FOR SHAPE CONSTRUCTION /////////////////////////////////////////////		
		/** This function changes the type of tool that is being applied on the panel 
		 	The shapeType parameter is used for the outlining mode.
		 * */
		public function shiftModes(modeName:String, callingObject:Object, shapeType:String=""):Boolean
		{
			var p:DrawPoint;
			var s:DrawSegment;
			var a:DrawAngle;
			// placing new points
			trace("shift modes to", modeName);
			if (modeName == "pen_mode")
			{
				if (!pen_mode)
				{
					pen_mode = true;
					drag_mode = false;
					delete_mode = false;
					pin_mode = false;
					parallel_mode = false;
					length_mode = false;
					angle_mode = false;
					right_mode = false;
					outline_mode = false;
					if (cursor_tool != null)
					{
						if (cursor_tool.hasEventListener(Event.COMPLETE)) cursor_tool.removeEventListener(Event.COMPLETE, handleToolComplete);
						this.removeChild(cursor_tool); 
						cursor_tool = null;
					}
					
					//updateCursorLook("pen");
					// go through each point and add event listener
					
					// add event to panel
					//if (!this.hasEventListener(MouseEvent.CLICK)) this.addEventListener(MouseEvent.CLICK, handleClickEmptyPanel);
					
					cursor_tool = new PenTool(drawPoints, this.oobshapes);
					cursor_tool.addEventListener(Event.COMPLETE, handleToolComplete);
					this.addChild(cursor_tool);
					
					
					// if we are calling from here call parent to tell menu
					if (callingObject == this) GeometryGameItem(parent).shiftModes(modeName, this);
					return true;	
				}
			} // dragging points
			else if (modeName == "drag_mode")
			{
				if (!drag_mode)
				{
					pen_mode = false;
					drag_mode = true;
					delete_mode = false;
					pin_mode = false;
					parallel_mode = false;
					length_mode = false;
					angle_mode = false;
					right_mode = false;
					outline_mode = false;
					if (cursor_tool != null)
					{
						if (cursor_tool.hasEventListener(Event.COMPLETE)) cursor_tool.removeEventListener(Event.COMPLETE, handleToolComplete);
						this.removeChild(cursor_tool); 
						cursor_tool = null;
					}
					// go through each point and add event listener
					
					
					
					cursor_tool = new DragTool(drawPoints, this.oobshapes);
					cursor_tool.addEventListener(Event.COMPLETE, handleToolComplete);
					this.addChild(cursor_tool);
					// if we are calling from here call parent to tell menu
					if (callingObject == this) GeometryGameItem(parent).shiftModes(modeName, this);
					return true;	
				} 
			}
			else if (modeName == "delete_mode")
			{
				if (!delete_mode)
				{
					pen_mode = false;
					drag_mode = false;
					delete_mode = true;
					pin_mode = false;
					parallel_mode = false;
					length_mode = false;
					angle_mode = false;
					right_mode = false;
					outline_mode = false;
					if (cursor_tool != null){
						if (cursor_tool.hasEventListener(Event.COMPLETE)) cursor_tool.removeEventListener(Event.COMPLETE, handleToolComplete);
						this.removeChild(cursor_tool); 
						cursor_tool = null; 
					}
					cursor_tool = new DeleteTool(drawPoints);
					cursor_tool.addEventListener(Event.COMPLETE, handleToolComplete);
					this.addChild(cursor_tool);
					
					
					// if we are calling from here call parent to tell menu
					if (callingObject == this) GeometryGameItem(parent).shiftModes(modeName, this);
					return true;	
				} 
			}
			else if (modeName == "pin_mode")
			{
				if (!pin_mode)
				{
					pen_mode = false;
					drag_mode = false;
					delete_mode = false;
					pin_mode = true;
					parallel_mode = false;
					length_mode = false;
					angle_mode = false;
					right_mode = false;
					outline_mode = false;
					if (cursor_tool != null){
						if (cursor_tool.hasEventListener(Event.COMPLETE)) cursor_tool.removeEventListener(Event.COMPLETE, handleToolComplete);
						this.removeChild(cursor_tool); 
						cursor_tool = null; 
					}
					cursor_tool = new PinTool(drawPoints);
					cursor_tool.addEventListener(Event.COMPLETE, handleToolComplete);
					this.addChild(cursor_tool);
					
					// if we are calling from here call parent to tell menu
					if (callingObject == this) GeometryGameItem(parent).shiftModes(modeName, this);
					return true;	
				} 
			}
			else if (modeName == "parallel_mode")
			{
				if (!parallel_mode)
				{
					pen_mode = false;
					drag_mode = false;
					delete_mode = false;
					pin_mode = false;
					parallel_mode = true;
					length_mode = false;
					angle_mode = false;
					right_mode = false;
					outline_mode = false;
					if (cursor_tool != null)
					{
						if (cursor_tool.hasEventListener(Event.COMPLETE)) cursor_tool.removeEventListener(Event.COMPLETE, handleToolComplete);
						this.removeChild(cursor_tool); 
						cursor_tool = null;
					}
					
					cursor_tool = new ParallelTool(drawSegments, oobshapes, tool_threshold_multiplier*parallel_threshold_degrees);
					cursor_tool.addEventListener(Event.COMPLETE, handleToolComplete);
					this.addChild(cursor_tool)
						
					// if we are calling from here call parent to tell menu
					if (callingObject == this) GeometryGameItem(parent).shiftModes(modeName, this);
					return true;	
				} 
			}
			else if (modeName == "length_mode")
			{
				if (!length_mode)
				{
					pen_mode = false;
					drag_mode = false;
					delete_mode = false;
					pin_mode = false;
					parallel_mode = false;
					length_mode = true;
					angle_mode = false;
					right_mode = false;
					outline_mode = false;
					if (cursor_tool != null){
						if (cursor_tool.hasEventListener(Event.COMPLETE)) cursor_tool.removeEventListener(Event.COMPLETE, handleToolComplete);
						this.removeChild(cursor_tool); 
						cursor_tool = null; 
					}
					
					cursor_tool = new LengthTool(drawSegments, oobshapes, tool_threshold_multiplier*length_threshold_pixels);
					cursor_tool.addEventListener(Event.COMPLETE, handleToolComplete);
					this.addChild(cursor_tool)
						
					// if we are calling from here call parent to tell menu
					if (callingObject == this) GeometryGameItem(parent).shiftModes(modeName, this);
					return true;	
				} 
			}
			else if (modeName == "angle_mode")
			{
				if (!length_mode)
				{
					pen_mode = false;
					drag_mode = false;
					delete_mode = false;
					pin_mode = false;
					parallel_mode = false;
					length_mode = false;
					angle_mode = true;
					right_mode = false;
					outline_mode = false;
					if (cursor_tool != null){
						if (cursor_tool.hasEventListener(Event.COMPLETE)) cursor_tool.removeEventListener(Event.COMPLETE, handleToolComplete);
						this.removeChild(cursor_tool); 
						cursor_tool = null; 
					}
					
					cursor_tool = new AngleTool(drawAngles, oobshapes, tool_threshold_multiplier*angle_threshold_degrees);
					cursor_tool.addEventListener(Event.COMPLETE, handleToolComplete);
					this.addChild(cursor_tool)
						
					// if we are calling from here call parent to tell menu
					if (callingObject == this) GeometryGameItem(parent).shiftModes(modeName, this);
					return true;	
				} 
			}else if (modeName == "right_mode")
			{
				if (!length_mode)
				{
					pen_mode = false;
					drag_mode = false;
					delete_mode = false;
					pin_mode = false;
					parallel_mode = false;
					length_mode = false;
					angle_mode = false;
					right_mode = true;
					outline_mode = false;
					if (cursor_tool != null){
						if (cursor_tool.hasEventListener(Event.COMPLETE)) cursor_tool.removeEventListener(Event.COMPLETE, handleToolComplete);
						this.removeChild(cursor_tool); 
						cursor_tool = null; 
					}
					
					cursor_tool = new RightTool(drawAngles, oobshapes, tool_threshold_multiplier*angle_threshold_degrees);
					cursor_tool.addEventListener(Event.COMPLETE, handleToolComplete);
					this.addChild(cursor_tool)
						
					// if we are calling from here call parent to tell menu
					if (callingObject == this) GeometryGameItem(parent).shiftModes(modeName, this);
					return true;	
				} 
			} else if (modeName == "outline_mode")
			{
				this.currentChildPolygonType = shapeType;
				if (!outline_mode)
				{
					pen_mode = false;
					drag_mode = false;
					delete_mode = false;
					pin_mode = false;
					parallel_mode = false;
					length_mode = false;
					angle_mode = false;
					right_mode = false;
					outline_mode = true;
					if (cursor_tool != null){
						if (cursor_tool.hasEventListener(Event.COMPLETE)) cursor_tool.removeEventListener(Event.COMPLETE, handleToolComplete);
						this.removeChild(cursor_tool); 
						cursor_tool = null; 
					}
					
					cursor_tool = new OutlineTool(closedPolygons);
					cursor_tool.addEventListener(Event.COMPLETE, handleToolComplete);
					this.addChild(cursor_tool)
						
					// if we are calling from here call parent to tell menu
					if (callingObject == this) GeometryGameItem(parent).shiftModes(modeName, this);
					return true;	
				} 
			}
			else {
				return false;
			}
			return false;
		}
	
		/** When the tool is completed this handler will shift to drag mode */
		
		// KEYBOARD
		private function handleKeyboard(event:KeyboardEvent):void
		{
			if (!outline_mode)
			{
				// Delete current point and associated segements
				if(event.keyCode == Keyboard.DELETE || event.keyCode == Keyboard.BACKSPACE)
				{
					if (!delete_mode)
					{
						shiftModes("delete_mode", this);
					} else
					{
						shiftModes("pen_mode", this);
					}
				} else if (event.keyCode == Keyboard.SHIFT)
				{ ////////// During shape construction 
					if (!drag_mode)
					{
						shiftModes("drag_mode", this);
					} else
					{
						shiftModes("pen_mode", this);
					}
				} 
				else if (event.keyCode == Keyboard.ESCAPE)
				{
					if (!pen_mode)
					{
						shiftModes("pen_mode", this);
					} else
					{
						shiftModes("drag_mode", this);
					}
				}
				else if (event.keyCode == Keyboard.SPACE)
				{
					if (!pin_mode)
					{
						shiftModes("pin_mode", this);
					} else
					{
						shiftModes("drag_mode", this);
					}
				}
					////////////////////////////////// PROPERTY TOOLS //////////////////////////////// 
				else if (event.keyCode == Keyboard.P)
				{ //////////////// Parallel ///////////////////////
					//make sure we are not in the middle of something else
					if (!dragging_point)
					{
						if (!parallel_mode)
						{
							shiftModes("parallel_mode", this);
						} else
						{
							shiftModes("drag_mode", this);
						}
					}
				}
				else if (event.keyCode == Keyboard.L)
				{ //////////////// Length ///////////////////////
					//make sure we are not in the middle of something else
					if (!dragging_point)
					{
						if (!length_mode)
						{
							shiftModes("length_mode", this);
						} else
						{
							shiftModes("drag_mode", this);
						}
					}
				}
				else if (event.keyCode == Keyboard.A)
				{ //////////////// Parallel ///////////////////////
					//make sure we are not in the middle of something else
					if (!dragging_point)
					{
						if (!angle_mode)
						{
							shiftModes("angle_mode", this);
						} else
						{
							shiftModes("drag_mode", this);
						}
					}
				}
				else if (event.keyCode == Keyboard.R)
				{ //////////////// Parallel ///////////////////////
					//make sure we are not in the middle of something else
					if (!dragging_point)
					{
						if (!right_mode)
						{
							shiftModes("right_mode", this);
						} else
						{
							shiftModes("drag_mode", this);
						}
					}
				} else if (event.keyCode == Keyboard.Z && event.ctrlKey)
				{ // UNDO
					stateChange("undo", this);
				}else if (event.keyCode == Keyboard.Y && event.ctrlKey)
				{ // UNDO
					stateChange("redo", this);
				}
			}
		}
		/** Updates cursor look */
		/*
		private function updateCursorLook(type:String):Boolean
		{
			if (cursor != null)
			{
				// for pen
				var pwidth:Number = 4;
				var pheight:Number = 20;
				// for drag
				var cdenom:Number = 1;
				var adenom:Number = 3;
				// for pin
				var twidth:Number = 3;
				var theight:Number = 15;
				var hwidth:Number = 8;
				var hheight:Number = 8;
				cursor.rotation = 0;
				switch(type)
				{
										
					case "pen":
						defaultCursorType = "pen";
						previousCursorType = currentCursorType;
						cursor.graphics.clear();
						// draw small circle
						cursor.graphics.beginFill(0xAAAAAA);
						cursor.graphics.drawEllipse(-pointsize/2, -pointsize/2, pointsize, pointsize);
						cursor.graphics.endFill();
						// draw pen
						cursor.graphics.lineStyle(pwidth/8,0x444444);
						cursor.graphics.beginFill(0x888888);
						cursor.graphics.moveTo(0,0);
						cursor.graphics.lineTo(pwidth/2,-pwidth);
						cursor.graphics.lineTo(-pwidth/2,-pwidth)
						cursor.graphics.lineTo(0,0);
						cursor.graphics.beginFill(0x8888FF);
						cursor.graphics.moveTo(pwidth/2,-pwidth);
						cursor.graphics.lineTo(pwidth/2,-pheight);
						cursor.graphics.lineTo(-pwidth/2,-pheight);
						cursor.graphics.lineTo(-pwidth/2,-pwidth)
						cursor.graphics.endFill();
						// handle
						cursor.graphics.beginFill(0x888888);
						cursor.graphics.drawEllipse(-pwidth/2,-pheight-pwidth/4,pwidth,pwidth/2);
						cursor.graphics.endFill();
						cursor.rotation=30;
						currentCursorType = "pen";
						break;
					case "pen-yes":
						previousCursorType = currentCursorType;
						cursor.graphics.clear();
						cursor.graphics.lineStyle(1,0x000000);
						cursor.graphics.drawEllipse(-pointsize/2, -pointsize/4, pointsize, pointsize);
						// draw pen
						cursor.graphics.lineStyle(pwidth/8,0x444444);
						cursor.graphics.beginFill(0x888888);
						cursor.graphics.moveTo(0,0);
						cursor.graphics.lineTo(pwidth/2,-pwidth);
						cursor.graphics.lineTo(-pwidth/2,-pwidth)
						cursor.graphics.lineTo(0,0);
						cursor.graphics.beginFill(0x8888FF);
						cursor.graphics.moveTo(pwidth/2,-pwidth);
						cursor.graphics.lineTo(pwidth/2,-pheight);
						cursor.graphics.lineTo(-pwidth/2,-pheight);
						cursor.graphics.lineTo(-pwidth/2,-pwidth)
						cursor.graphics.endFill();
						// handle
						cursor.graphics.beginFill(0x888888);
						cursor.graphics.drawEllipse(-pwidth/2,-pheight-pwidth/4,pwidth,pwidth/2);
						cursor.graphics.endFill();
						cursor.rotation=30;
						currentCursorType = "pen-yes";
						break;
					case "pen-no":
						previousCursorType = currentCursorType;
						cursor.graphics.clear();
						cursor.graphics.lineStyle(1,0x000000);
						cursor.graphics.drawEllipse(-pointsize/2, -pointsize/2, pointsize, pointsize);
						cursor.graphics.lineStyle(2,0xFF0000);
						cursor.graphics.moveTo(-pointsize/2*Math.sqrt(2)/2,-pointsize/2*Math.sqrt(2)/2);
						cursor.graphics.lineTo(pointsize/2*Math.sqrt(2)/2,pointsize/2*Math.sqrt(2)/2);
						cursor.graphics.moveTo(pointsize/2*Math.sqrt(2)/2,-pointsize/2*Math.sqrt(2)/2);
						cursor.graphics.lineTo(-pointsize/2*Math.sqrt(2)/2,pointsize/2*Math.sqrt(2)/2);
						// draw pen
						cursor.graphics.lineStyle(pwidth/8,0x444444);
						cursor.graphics.beginFill(0x888888);
						cursor.graphics.moveTo(0,0);
						cursor.graphics.lineTo(pwidth/2,-pwidth);
						cursor.graphics.lineTo(-pwidth/2,-pwidth)
						cursor.graphics.lineTo(0,0);
						cursor.graphics.beginFill(0x8888FF);
						cursor.graphics.moveTo(pwidth/2,-pwidth);
						cursor.graphics.lineTo(pwidth/2,-pheight);
						cursor.graphics.lineTo(-pwidth/2,-pheight);
						cursor.graphics.lineTo(-pwidth/2,-pwidth)
						cursor.graphics.endFill();
						// handle
						cursor.graphics.beginFill(0x888888);
						cursor.graphics.drawEllipse(-pwidth/2,-pheight-pwidth/4,pwidth,pwidth/2);
						cursor.graphics.endFill();
						cursor.rotation=30;
						currentCursorType = "pen-yes";
						break;
					case "drag":
						defaultCursorType = "drag";
						previousCursorType = currentCursorType;
						cursor.graphics.clear();
						cursor.graphics.lineStyle(1,0x000000);
						cursor.graphics.moveTo(0,-pointsize/cdenom);
						cursor.graphics.lineTo(0,pointsize/cdenom);
						cursor.graphics.moveTo(-pointsize/cdenom,0);
						cursor.graphics.lineTo(pointsize/cdenom,0);
						//arrowheads
						cursor.graphics.lineStyle(2,0x000000);
						//top
						cursor.graphics.moveTo(0,-pointsize/cdenom);
						cursor.graphics.lineTo(-pointsize/adenom,-pointsize/cdenom+pointsize/adenom);
						cursor.graphics.moveTo(0,-pointsize/cdenom);
						cursor.graphics.lineTo(pointsize/adenom,-pointsize/cdenom+pointsize/adenom);
						//bottom
						cursor.graphics.moveTo(0,pointsize/cdenom);
						cursor.graphics.lineTo(-pointsize/adenom,pointsize/cdenom-pointsize/adenom);
						cursor.graphics.moveTo(0,pointsize/cdenom);
						cursor.graphics.lineTo(pointsize/adenom,pointsize/cdenom-pointsize/adenom);
						//left
						cursor.graphics.moveTo(-pointsize/cdenom,0);
						cursor.graphics.lineTo(-pointsize/cdenom+pointsize/adenom,-pointsize/adenom);
						cursor.graphics.moveTo(-pointsize/cdenom,0);
						cursor.graphics.lineTo(-pointsize/cdenom+pointsize/adenom, pointsize/adenom);
						//right
						cursor.graphics.moveTo(pointsize/cdenom,0);
						cursor.graphics.lineTo(pointsize/cdenom-pointsize/adenom,-pointsize/adenom);
						cursor.graphics.moveTo(pointsize/cdenom,0);
						cursor.graphics.lineTo(pointsize/cdenom-pointsize/adenom, pointsize/adenom);
						
						currentCursorType = "drag";
						break;	
					case "drag-yes":
						previousCursorType = currentCursorType;
						cursor.graphics.clear();
						cursor.graphics.lineStyle(1,0x000000);
						cursor.graphics.drawEllipse(-pointsize/2, -pointsize/2, pointsize, pointsize);
						//arrowheads
						cursor.graphics.lineStyle(2,0x000000);
						//top
						cursor.graphics.moveTo(0,-pointsize/cdenom);
						cursor.graphics.lineTo(-pointsize/adenom,-pointsize/cdenom+pointsize/adenom);
						cursor.graphics.moveTo(0,-pointsize/cdenom);
						cursor.graphics.lineTo(pointsize/adenom,-pointsize/cdenom+pointsize/adenom);
						//bottom
						cursor.graphics.moveTo(0,pointsize/cdenom);
						cursor.graphics.lineTo(-pointsize/adenom,pointsize/cdenom-pointsize/adenom);
						cursor.graphics.moveTo(0,pointsize/cdenom);
						cursor.graphics.lineTo(pointsize/adenom,pointsize/cdenom-pointsize/adenom);
						//left
						cursor.graphics.moveTo(-pointsize/cdenom,0);
						cursor.graphics.lineTo(-pointsize/cdenom+pointsize/adenom,-pointsize/adenom);
						cursor.graphics.moveTo(-pointsize/cdenom,0);
						cursor.graphics.lineTo(-pointsize/cdenom+pointsize/adenom, pointsize/adenom);
						//right
						cursor.graphics.moveTo(pointsize/cdenom,0);
						cursor.graphics.lineTo(pointsize/cdenom-pointsize/adenom,-pointsize/adenom);
						cursor.graphics.moveTo(pointsize/cdenom,0);
						cursor.graphics.lineTo(pointsize/cdenom-pointsize/adenom, pointsize/adenom);
						currentCursorType = "drag-yes";
						break;
					case "drag-no":
						previousCursorType = currentCursorType;
						cursor.graphics.clear();
						cursor.graphics.lineStyle(1,0x000000);
						cursor.graphics.drawEllipse(-pointsize/2, -pointsize/2, pointsize, pointsize);
						cursor.graphics.lineStyle(1.5,0xFF0000);
						cursor.graphics.moveTo(-enhancedsize/2*Math.sqrt(2)/2,-enhancedsize/2*Math.sqrt(2)/2);
						cursor.graphics.lineTo(enhancedsize/2*Math.sqrt(2)/2,enhancedsize/2*Math.sqrt(2)/2);
						cursor.graphics.moveTo(enhancedsize/2*Math.sqrt(2)/2,-enhancedsize/2*Math.sqrt(2)/2);
						cursor.graphics.lineTo(-enhancedsize/2*Math.sqrt(2)/2,enhancedsize/2*Math.sqrt(2)/2);
						currentCursorType = "drag-no";
						break;
					case "delete":
						defaultCursorType = "delete";
						previousCursorType = currentCursorType;
						cursor.graphics.clear();
						cursor.graphics.lineStyle(2,0x000000);
						cursor.graphics.moveTo(-pointsize/2*Math.sqrt(2)/2,-pointsize/2*Math.sqrt(2)/2);
						cursor.graphics.lineTo(pointsize/2*Math.sqrt(2)/2,pointsize/2*Math.sqrt(2)/2);
						cursor.graphics.moveTo(pointsize/2*Math.sqrt(2)/2,-pointsize/2*Math.sqrt(2)/2);
						cursor.graphics.lineTo(-pointsize/2*Math.sqrt(2)/2,pointsize/2*Math.sqrt(2)/2);
						currentCursorType = "delete";
						break;
					case "delete-yes":
						previousCursorType = currentCursorType;
						cursor.graphics.clear();
						cursor.graphics.lineStyle(3,0x000000);
						cursor.graphics.moveTo(-enhancedsize/2*Math.sqrt(2)/2,-enhancedsize/2*Math.sqrt(2)/2);
						cursor.graphics.lineTo(enhancedsize/2*Math.sqrt(2)/2,enhancedsize/2*Math.sqrt(2)/2);
						cursor.graphics.moveTo(enhancedsize/2*Math.sqrt(2)/2,-enhancedsize/2*Math.sqrt(2)/2);
						cursor.graphics.lineTo(-enhancedsize/2*Math.sqrt(2)/2,enhancedsize/2*Math.sqrt(2)/2);
						cursor.graphics.lineStyle(1,0xFFFF0);
						cursor.graphics.moveTo(-enhancedsize/2*Math.sqrt(2)/2,-enhancedsize/2*Math.sqrt(2)/2);
						cursor.graphics.lineTo(enhancedsize/2*Math.sqrt(2)/2,enhancedsize/2*Math.sqrt(2)/2);
						cursor.graphics.moveTo(enhancedsize/2*Math.sqrt(2)/2,-enhancedsize/2*Math.sqrt(2)/2);
						cursor.graphics.lineTo(-enhancedsize/2*Math.sqrt(2)/2,enhancedsize/2*Math.sqrt(2)/2);
						currentCursorType = "delete-yes";
						break;
					case "delete-no":
						defaultCursorType = "delete";
						previousCursorType = currentCursorType;
						cursor.graphics.clear();
						cursor.graphics.lineStyle(2,0xFF0000);
						cursor.graphics.moveTo(-pointsize/2*Math.sqrt(2)/2,-pointsize/2*Math.sqrt(2)/2);
						cursor.graphics.lineTo(pointsize/2*Math.sqrt(2)/2,pointsize/2*Math.sqrt(2)/2);
						cursor.graphics.moveTo(pointsize/2*Math.sqrt(2)/2,-pointsize/2*Math.sqrt(2)/2);
						cursor.graphics.lineTo(-pointsize/2*Math.sqrt(2)/2,pointsize/2*Math.sqrt(2)/2);
						currentCursorType = "delete-no";
						break;
					case "pin":
						defaultCursorType = "pin";
						//draw a pin, literally
						previousCursorType = currentCursorType;
						// tip						
						cursor.graphics.clear();
						cursor.graphics.lineStyle(twidth/8,0x666666);
						cursor.graphics.beginFill(0xCCCCCC);
						cursor.graphics.moveTo(0,0);
						cursor.graphics.lineTo(twidth/2,-twidth);
						cursor.graphics.lineTo(twidth/2,-theight);
						cursor.graphics.lineTo(-twidth/2,-theight);
						cursor.graphics.lineTo(-twidth/2,-twidth)
						cursor.graphics.lineTo(0,0);
						cursor.graphics.endFill();
						// handle
						cursor.graphics.beginFill(0xFFFFFF);
						cursor.graphics.drawEllipse(-hwidth/2,-theight,hwidth,hwidth/2);
						cursor.graphics.endFill();
						currentCursorType = "pin";
						break;
					case "pin-yes":
						defaultCursorType = "pin";
						//draw a pin, literally
						previousCursorType = currentCursorType;
						// handle
						cursor.graphics.clear();
						cursor.graphics.lineStyle(twidth/8,0x666666);
						cursor.graphics.beginFill(0xFFFFFF);
						cursor.graphics.drawEllipse(-hwidth/2,-hwidth/2,hwidth,hwidth);
						cursor.graphics.endFill();
						currentCursorType = "pin-yes";
						break;
					case "pin-no":
						defaultCursorType = "pin";
						//draw a pin, literally
						previousCursorType = currentCursorType;
						// handle
						cursor.graphics.clear();
						cursor.graphics.lineStyle(twidth/8,0x666666);
						cursor.graphics.beginFill(0xFFFFFF);
						cursor.graphics.drawEllipse(-hwidth/2,-hwidth/2,hwidth,hwidth);
						cursor.graphics.endFill();
						cursor.graphics.lineStyle(1.5,0xFF0000);
						cursor.graphics.moveTo(-hwidth*Math.sqrt(2)/2,-hwidth*Math.sqrt(2)/2);
						cursor.graphics.lineTo(hwidth*Math.sqrt(2)/2,hwidth*Math.sqrt(2)/2);
						cursor.graphics.moveTo(hwidth*Math.sqrt(2)/2,-hwidth*Math.sqrt(2)/2);
						cursor.graphics.lineTo(-hwidth*Math.sqrt(2)/2,hwidth*Math.sqrt(2)/2);						
						currentCursorType = "pin-no";
						break;
					default:
						return false;
						break;
				}
				return true;
			}
			return false;
		}
		*/
////////////////////////////////////////////// SAVING AND XML FUNCTIONS ///////////////////////////////////////////////		
		
		/** This function adds a new set of nodes to the xml, at the current level */
		public function saveNewDrawState(cmd:String):void
		{
			currentDrawState=totalDrawStates; // append to the end
			totalDrawStates++;
			// create a node for a new draw state, add points then segments
			var node:XML = new XML();
			node = 
			<drawstate num={currentDrawState}>
			</drawstate>;
			node.@command = cmd;
			// go through points to append to node
			
			for (var i:int=0; i < drawPoints.length; i++)
			{
				var p:DrawPoint = drawPoints[i];
				var pnode:XML = new XML();
				pnode = <point />;
				pnode.@id = p.id;
				pnode.@x = String(p.x);
				pnode.@y = String(p.y);
				if (p.pinned) pnode.@pinned = "true";
				if (p.fixed) pnode.@fixed = "true";
				//<point id=p{p.id} x={p.x} y={p.y} pinned={p.pinned} fixed={p.fixed} />;
				node.appendChild(pnode);
			}
			
			for (i=0; i < drawSegments.length; i++)
			{
				var s:DrawSegment = drawSegments[i];
				var snode:XML = new XML();
				snode = <segment />;
				snode.@id = s.id;
				snode.@p1 = String(s.point1.id);
				snode.@p2 = String(s.point2.id);
				
				//<segment id=s{s.id} x={s.x} y={s.y} />
				node.appendChild(snode);
			}
			//append to itemxml
			xml.appendChild(node);
			// save this file
			ShapeProductionTask(parent.parent.parent.parent).saveXML();
		}
		
		public function stateChange (cmd:String, callingObject:Object):Boolean
		{
			switch (cmd)
			{
				case "undo":
					gotoPreviousState();
					break;
				case "redo":
					gotoNextState();
					break;
				case "first":
					gotoFirstState();
					break;
				case "last":
					gotoLastState();
					break;
				default:
					trace("Not a valid state");
					return false;
			}
			if (callingObject == this) GeometryGameItem(parent).stateChange(cmd, callingObject);
			return true;
		}
		
		///// UN-DOING AND RE-DOING
		public function gotoPreviousState():Boolean
		{
			if (currentDrawState > 0)
			{
				clear();
				currentDrawState--;
				drawPointsFromXML(currentDrawState);
				drawSegmentsFromXML(currentDrawState);
				shiftModes("clear_mode", this);
				if (!shiftModes(defaultCursorType+"_mode", this)) shiftModes("pen_mode", this);
				return true;
			} else
			{
				return false;
			}
		}
		public function gotoFirstState():Boolean
		{
			if (currentDrawState > 0)
			{
				clear();
				currentDrawState=0;
				drawPointsFromXML(currentDrawState);
				drawSegmentsFromXML(currentDrawState);
				shiftModes("clear_mode", this);
				if (!shiftModes(defaultCursorType+"_mode", this)) shiftModes("pen_mode", this);
				return true;
			} else
			{
				return false;
			}
		}
		public function gotoNextState():Boolean
		{
			if (currentDrawState < totalDrawStates-1)
			{
				clear();
				currentDrawState++;
				drawPointsFromXML(currentDrawState);
				drawSegmentsFromXML(currentDrawState);
				shiftModes("clear_mode", this);
				if (!shiftModes(defaultCursorType+"_mode", this)) shiftModes("pen_mode", this);
				return true;
			} else
			{
				return false;
			}
		}
		public function gotoLastState():Boolean
		{
			if (currentDrawState < totalDrawStates-1)
			{
				clear();
				currentDrawState = totalDrawStates-1;
				drawPointsFromXML(currentDrawState);
				drawSegmentsFromXML(currentDrawState);
				shiftModes("clear_mode", this);
				if (!shiftModes(defaultCursorType+"_mode", this)) shiftModes("pen_mode", this);
				return true;
			} else
			{
				return false;
			}
		}
	}	
}